<?php
abstract class ModuleGm
{
    /**
     * @var array
     */
    protected static $insertOrmData = array();

    /**
     * Initialize.
     */
    protected function __construct()
    {
    }

    const ADMIN_DIR_NAME = 'gm';
    const PAGE_DIR_NAME = 'page';
    const ACTION_CLASS_TRAIL = 'Action';
    const ACTION_CLASS_FILE_TRAIL = 'Action.class.php';

    /**
     * Loop "SzSystem::$APP_ROOT/lib/controller/action/*", get all sub dir names.
     * Except "data".
     *
     * @throws SzException 30601
     * @return array
     * <pre>
     * array(
     *     $moduleName => array(),
     *     ...
     * )
     * </pre>
     */
    public function loadApiModules()
    {
        $modules = array();

        $actionPath = SzSystem::$APP_ROOT . '/lib/controller/action';

        if ($handle = opendir($actionPath)) {
            while (($file = readdir($handle)) !== false) {
                if ($file == '.' || $file == '..') {
                    // continue to next loop if element is '.' or '..'
                    continue;
                }
                if (is_dir("{$actionPath}/{$file}")
                    && $file != self::ADMIN_DIR_NAME
                    && $file != self::PAGE_DIR_NAME
                ) {
                    // target element is folder & not data
                    $modules[$file] = array();
                }
            }
            closedir($handle);
        } else {
            throw new SzException(30601, array($actionPath), ModuleGmConst::MODULE_NAME);
        }

        return $modules;
    }

    /**
     * Loop "SzSystem::$APP_ROOT/lib/controller/action/$moduleName/*", get all action names.
     *
     * @param string $moduleName
     * @throws SzException 30601
     * @return array
     * <pre>
     * array(
     *     $apiName => array(),
     *     ...
     * )
     * </pre>
     */
    public function loadModuleApis($moduleName)
    {
        $apis = array();

        if ($moduleName == self::ADMIN_DIR_NAME
            || $moduleName == self::PAGE_DIR_NAME) {
            // data | page is not allowed to be retrieved
            return $apis;
        }

        $actionPath = SzSystem::$APP_ROOT . '/lib/controller/action/' . $moduleName;

        if ($handle = opendir($actionPath)) {
            while (($file = readdir($handle)) !== false) {
                if ($file == '.' || $file == '..') {
                    // continue to next loop if element is '.' or '..'
                    continue;
                }
                if (is_file("{$actionPath}/{$file}")) {
                    $trailIndex = strpos($file, self::ACTION_CLASS_FILE_TRAIL);
                    if ($trailIndex === false) {
                        continue; // not action class file
                    }
                    // target element is action class
                    $apis[substr($file, 0, $trailIndex)] = array();
                }
            }
            closedir($handle);
        } else {
            throw new SzException(30601, array($actionPath), ModuleGmConst::MODULE_NAME);
        }

        return $apis;
    }

    /**
     * Load api action detail, name & params.
     *
     * @param $moduleName
     * @param $actionName
     * @throws SzException 30601
     * @return array
     * <pre>
     * array(
     *     array("name" => $paramName, "type" => $paramType),
     *     ...
     * )
     * </pre>
     */
    public function loadApiDetail($moduleName, $actionName)
    {
        $detail = array();

        $actionPath = SzSystem::$APP_ROOT . '/lib/controller/action/' . $moduleName . '/' . $actionName . self::ACTION_CLASS_FILE_TRAIL;

        if (!file_exists($actionPath)) {
            throw new SzException(30601, array($actionPath), ModuleGmConst::MODULE_NAME);
        }

        // load reflection class
        $actionClassName = $actionName . self::ACTION_CLASS_TRAIL;
        $actionClass = new ReflectionClass($actionClassName);

        // load reflection class 'execute' method
        $method = $actionClass->getMethod('execute');
        $methodParams = $method->getParameters();
        if ($methodParams) {
            // load reflection class 'paramTypes' property
            $paramTypes = $actionClass->getProperty('paramTypes');
            $paramTypes->setAccessible(true);
            $paramTypes = $paramTypes->getValue(new $actionClassName());

            foreach ($methodParams as $index => $param) {
                $detail[] = array(
                    'name' => $param->getName(),
                    'type' => SzUtility::checkArrayKey($index, $paramTypes) ?
                            $paramTypes[$index] : 'TYPE_UNKNOW'
                );
            }
        }

        return $detail;
    }

    /**
     * Load all orm setting names. Loop all the orm.config.php files.
     *
     * @return array
     * <pre>
     * array(
     *     $ormName => array(
     *         $columnName => null,
     *         ...
     *     ),
     *     ...
     * )
     * </pre>
     */
    public function loadOrms()
    {
        $orms = array();

        // handle app orm settings
        $appOrms = SzConfig::get()->loadAppConfig('orm');
        if ($appOrms) {
            foreach ($appOrms as $ormName => $ormSetting) {
                $columns = array();
                foreach ($ormSetting['columns'] as $columnName) {
                    $columns[$columnName] = null;
                }
                $orms[$ormName] = $columns;
            }
        }

        // handle module orm settings
        $moduleNames = SzConfig::get()->loadAppConfig('app', 'MODULE_VERS');
        if ($moduleNames) {
            $moduleNames = array_keys($moduleNames); // array($moduleName => $version) => array($moduleName)
            foreach ($moduleNames as $moduleName) {
                $moduleOrms = SzConfig::get()->loadModuleConfig($moduleName, 'orm');
                if (!$moduleOrms) {
                    continue;
                }
                foreach ($moduleOrms as $ormName => $ormSetting) {
                    $columns = array();
                    foreach ($ormSetting['columns'] as $columnName) {
                        $columns[$columnName] = null;
                    }
                    $orms[$ormName] = $columns;
                }
            }
        }

        return $orms;
    }

    /**
     * Load orm data according to $ormName.
     *
     * @param int $userId
     * @param string $ormName
     * @return array
     */
    public function loadOrmData($userId, $ormName)
    {
        $data = array();

        $model = SzPersister::get()->getModel($ormName);

        if ($model->hasListMode()) {
            $voList = SzPersister::get()->getVoList($userId, $ormName);
            $data = $voList->toArray();
        } else {
            $vo = SzPersister::get()->getVo($userId, $ormName);
            if ($vo) {
                $data[$model->getColumnValue($vo, $model->getPkColumn())] = $vo->toArray();
            }
        }

        if ($ormName == ModuleItemConst::ORM_NAME_ITEM && $data) {
            $itemList = array(); // itemId => itemVo
            foreach ($data as $items) {
                foreach ($items as $itemId => $item) {
                    $itemList[$itemId] = $item;
                }
            }
            $data = $itemList;
        }

        return $data;
    }

    /**
     * Insert orm data into database.
     *
     * @param string $ormName
     * @param array $data
     * @throws SzException 30602, 30603
     * @return array
     * <pre>
     * array(
     *     'pk' => int: pk value of new inserted record | false: insert failed
     *     'data' => vo data array
     * )
     * </pre>
     */
    public function insertOrmData($ormName, $data)
    {
        $result = false;

        if (!is_array($data)) {
            throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        $model = SzPersister::get()->getModel($ormName);

        $params = array();
        foreach ($model->getColumns() as $columnName) { // loop according to orm columns order
            if (!SzUtility::checkArrayKey($columnName, $data, true)) {
                throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
            }
            $params[] = $data[$columnName];
        }
        $params[] = true; // insert flag

        $reflection = new ReflectionClass($model->getVoClassName());
        /* @var SzAbstractVo $vo */
        $vo = $reflection->newInstanceArgs($params);

        if ($model->getDbType() == SzAbstractDb::DB_TYPE_MYSQL) {
            /* @var SzMySqlModel $model */
            list($affectedRows, $insertedId) = $model->insert($vo);
            if ($affectedRows) {
                if ($model->hasAutoIncrementId()) {
                    $model->setColumnValue($vo, $model->getAutoIncrColumn(), $insertedId);
                }

                $vo->removeInsertFlag();
                if ($model->hasListMode()) {
                    $shardKey = $model->getColumnValue($vo, $model->getShardColumn());
                    if (!$shardKey) {
                        throw new SzException(10509);
                    }
                    $selectedList = $model->select($shardKey)->getList();
                    $model->setListOfVoCache(array($vo), $selectedList);
                } else {
                    $model->setVoCache($vo);
                }

                $result = $model->getColumnValue($vo, $model->getPkColumn());
            } else {
                throw new SzException(30603, null, ModuleGmConst::MODULE_NAME); // failed
            }
        } else if ($model->getDbType() == SzAbstractDb::DB_TYPE_REDIS) {
            /* @var SzRedisModel $model */
            $vo->removeInsertFlag();
            $newAutoIncrId = $model->genAutoIncrId();
            $model->setColumnValue($vo, $model->getAutoIncrColumn(), $newAutoIncrId);
            $model->save($vo);
            $result = $model->getColumnValue($vo, $model->getPkColumn());
        }

        return array('pk' => $result, 'data' => $vo->toArray());
    }

    /**
     * Update orm data into database.
     *
     * @param string $ormName
     * @param array $data
     * @throws SzException 30602, 30604
     * @return array
     * <pre>
     * array(
     *     'succeed' => boolean,
     *     'data' => vo data array
     * )
     * </pre>
     */
    public function updateOrmData($ormName, $data)
    {
        $result = false;

        if (!is_array($data)) {
            throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        $model = SzPersister::get()->getModel($ormName);

        $cacheColumnName = $model->getColumnName($model->getCacheColumn());
        $shardColumnName = $model->getColumnName($model->getShardColumn());
        $pkColumnName = $model->getColumnName($model->getPkColumn());


        if ($shardColumnName == null) {
            if (!SzUtility::checkArrayKey(array($pkColumnName), $data, true)) {
                throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
            }
            $shardVal   = null;
        } else {
            if (!SzUtility::checkArrayKey(array($shardColumnName, $pkColumnName), $data, true)) {
                throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
            }
            $shardVal = $data[$shardColumnName];
        }

        $cacheVal   = $data[$cacheColumnName];
        $pkVal      = $data[$pkColumnName];

        /* @var SzAbstractVo $vo */
        $vo = null;
        if ($model->hasListMode()) {
            /* @var SzAbstractVoList $voList */
            $voList = $model->retrieve($shardVal, $cacheVal);
            $vo = $voList->getElement($pkVal);
        } else {
            $vo = $model->retrieve($shardVal, $cacheVal);
        }
        if (!$vo) {
            throw new SzException(30604, null, ModuleGmConst::MODULE_NAME); // record not found
        }

        foreach ($model->getColumns() as $columnId => $columnName) { // loop according to orm columns order
            if (!SzUtility::checkArrayKey($columnName, $data, true)) {
                throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
            }
            if ($model->isJsonColumn($columnId)) {
                $columnValue = json_decode($data[$columnName], true);
            } else {
                $columnValue = $data[$columnName];
            }
            $model->setColumnValue($vo, $columnId, $columnValue);
        }

        if ($model->getDbType() == SzAbstractDb::DB_TYPE_MYSQL) {
            /* @var SzMySqlModel $model */
            list($affectedRows) = $model->update($vo);
            if ($affectedRows) {
                if ($model->hasListMode()) {
                    $shardKey = $model->getColumnValue($vo, $model->getShardColumn());
                    if (!$shardKey) {
                        throw new SzException(10509);
                    }
                    $selectedList = $model->select($shardKey)->getList();
                    $model->setListOfVoCache(array($vo), $selectedList);
                } else {
                    $model->setVoCache($vo);
                }
                $result = true;
            }
        } else if ($model->getDbType() == SzAbstractDb::DB_TYPE_REDIS) {
            /* @var SzRedisModel $model */
            $model->save($vo);
            $result = true;
        }

        return array('succeed' => $result, 'data' => $vo->toArray());
    }

    /**
     * Delete orm data into database.
     *
     * @param string $ormName
     * @param array $data
     * @throws SzException 30602, 30604
     * @return boolean
     */
    public function deleteOrmData($ormName, $data)
    {
        $result = false;

        if (!is_array($data)) {
            throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        $model = SzPersister::get()->getModel($ormName);

        $cacheColumnName = $model->getColumnName($model->getCacheColumn());
        $shardColumnName = $model->getColumnName($model->getShardColumn());
        $pkColumnName = $model->getColumnName($model->getPkColumn());

        if (!SzUtility::checkArrayKey(array($shardColumnName, $pkColumnName), $data, true)) {
            throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        /* @var SzAbstractVo $vo */
        $vo = null;
        if ($model->hasListMode()) {
            /* @var SzAbstractVoList $voList */
            $voList = $model->retrieve($data[$shardColumnName], $data[$cacheColumnName]);
            $vo = $voList->getElement($data[$pkColumnName]);
        } else {
            $vo = $model->retrieve($data[$shardColumnName], $data[$cacheColumnName]);
        }
        if (!$vo) {
            throw new SzException(30604, null, ModuleGmConst::MODULE_NAME); // record not found
        }

        if ($model->getDbType() == SzAbstractDb::DB_TYPE_MYSQL) {
            /* @var SzMySqlModel $model */
            list($affectedRows) = $model->delete($vo);
            if ($affectedRows) {
                $model->deleteVoCache($vo);
                $result = true;
            }
        } else if ($model->getDbType() == SzAbstractDb::DB_TYPE_REDIS) {
            /* @var SzRedisModel $model */
            $model->delete($vo);
            $result = true;
        }

        return $result;
    }

    /**
     * create a new player data
     *
     * @param int $platformId
     * @return SzResponse
     */
    abstract protected function createPlayer($platformId);

    /**
     * create a new player data
     *
     * @param int $platformId
     * @param boolean $onlyCache default false
     * @return SzResponse
     */
    abstract protected function deletePlayer($platformId, $onlyCache);

    /**
     * Delete player game data cache.
     *
     * @param int $platformId
     * @return boolean
     */
    abstract protected function deletePlayerCache($platformId);

    /**
     * Flush all cache servers.
     *
     * @param string $password
     * @throws SzException 30600
     * @return boolean
     */
    public function flushCache($password)
    {
        if ($password != ModuleGmConst::GM_PASSWORD) {
            throw new SzException(30600, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        $cacheConfig = SzConfig::get()->loadAppConfig('cache');

        // memcache
        $memcacheConfigs = array();
        if (SzUtility::checkArrayKey('App_Memcached', $cacheConfig) && $cacheConfig['App_Memcached']) {
            $memcacheConfigs = $cacheConfig['App_Memcached'];
        }
        if (SzUtility::checkArrayKey('Static_Memcached', $cacheConfig) && $cacheConfig['Static_Memcached']) {
            foreach ($cacheConfig['Static_Memcached'] as $config) {
                if (!in_array($config, $memcacheConfigs)) {
                    $memcacheConfigs[] = $config;
                }
            }
        }
        foreach ($memcacheConfigs as $config) {
            $memcache = new Memcached();
            $memcache->addServer($config[0], $config[1]);
            $memcache->flush();
        }

        // redis
        $redisConfigs = array();
        if (SzUtility::checkArrayKey('App_Redis', $cacheConfig) && $cacheConfig['App_Redis']) {
            $redisConfigs = $cacheConfig['App_Redis'];
        }
        if (SzUtility::checkArrayKey('Static_Redis', $cacheConfig) && $cacheConfig['Static_Redis']) {
            foreach ($cacheConfig['Static_Redis'] as $config) {
                if (!in_array($config, $redisConfigs)) {
                    $redisConfigs[] = $config;
                }
            }
        }
        foreach ($redisConfigs as $config) {
            $redis = new SzRedisCache($config);
            $redis->flush();
        }

        return true;
    }

    /**
     * export tar.gz file
     *
     * @param int $userId
     * @return array $file
     * <pre>
     * array(
     *     $fileName,
     *     $filePath
     * )
     * </pre>
     */
    public function export($userId)
    {
        $ignoreOrm = SzConfig::get()->loadAppConfig(ModuleGmConst::CONFIG_GM_IMPORT_IGNORE_ORM, null, true);
        $dumpPath = $this->getDumpPath($userId);
        $ormList = $this->loadOrms();
        foreach ($ormList as $ormName => $ormColumns) {
            if (in_array($ormName, $ignoreOrm)) {
                continue;
            }
            $voListArray = $this->loadOrmData($userId, $ormName);
            $this->dumpJson("{$dumpPath}/json", "{$ormName}.json", $voListArray);
        }

        $tarName = $this->getDumpTarName($userId);
        chdir($dumpPath);
        self::run(self::genTarCreateCmd("./{$tarName}", './json'));

        return array($tarName, $dumpPath);
    }


    /**
     * import tar.gz file
     *
     * @param int $userId
     * @param string $password
     * @throws SzException 30600, 30608
     * @return array $file
     * <pre>
     * array(
     *     $fileName,
     *     $filePath
     * )
     * </pre>
     */
    public function import($userId, $password)
    {
        if ($password != ModuleGmConst::GM_PASSWORD) {
            throw new SzException(30600, null, ModuleGmConst::MODULE_NAME); // invalid $data
        }

        $cacheInfo = SzContextFactory::get()->getAppCache()->get('ADMIN-IMPORT:' . $userId);
        if ($cacheInfo) {
            echo 'data importing, please don\'t import again, userId:' . $userId;
            exit;
        }
        list($filePath, $fileName) = SzFile::checkUploadedFile();

        // validate file type
        if (false === strpos($fileName, '.tar.gz')) {
            throw new SzException(30608, null, ModuleGmConst::MODULE_NAME);
        }
        // extract tar file
        $tempPath = $this->getDataBackupPath($userId, false);
        chdir($tempPath);
        self::run(self::genTarExtractCmd($filePath, $tempPath));
        $files = scandir("{$tempPath}/json");
        /**
         * array (
         *     0 => '.',
         *     1 => '..',
         *     2 => 'file1.json',
         *     3 => 'file2.json',
         *     4 => ...,
         * )
         */
        unset($files[0], $files[1]);

        SzLogger::get()->debug('[Admin Import] import tar.gz and restore start');
        SzContextFactory::get()->getAppCache()->set('ADMIN-IMPORT:' . $userId, SzTime::setTime());

        //restore data
        if ($files) {
            // 仅上传成功，才删除玩家数据
            GameService::get()->deletePlayerGameData($userId);

            foreach ($files as $fileName) {
                $return = $this->restoreDataFromJsonFile($fileName, "{$tempPath}/json/{$fileName}");
                if (!is_null($return)) {
                    list($ormName, $pkData) = each($return);
                    self::$insertOrmData[$ormName] = $pkData;
                }
            }

            $dependConfigs = SzConfig::get()->loadAppConfig(ModuleGmConst::CONFIG_GM_IMPORT_DEPEND, null, true);
            foreach ($dependConfigs as $ormName => $dependConfig) {
                $fileName = $ormName . '.json';
                if (in_array($fileName, $files)) {
                    $ormList = $this->restoreDataFromJsonFile($fileName, "{$tempPath}/json/{$fileName}", true);
                    list($ormName, $pkData) = each($ormList);
                    self::$insertOrmData[$ormName] = $pkData;
                }
            }
        }

        SzLogger::get()->debug('[Admin Import] import tar.gz and restore end');
        SzContextFactory::get()->getAppCache()->delete('ADMIN-IMPORT:' . $userId);

        return true;
    }

    /**
     * Make & return the data backup path.
     *
     * <pre>
     * "/tmp/fs2-import-{$userId}-" . SzTime::getYmdDate() . "-" . uniqid()
     * "/tmp/fs2-import-{$userId}-" . SzTime::getYmdDate() . "-" . uniqid() . "/backups"
     * </pre>
     *
     * @param string $userId
     * @param boolean $needBackupDir default true
     * @return string $path without backups
     */
    private function getDataBackupPath($userId, $needBackupDir = true)
    {
        $rand = uniqid();
        $path = "/tmp/fs2-import-{$userId}-" . SzTime::getYmdDate() . "-{$rand}";
        self::run(self::genMkdirCmd($path));
        if ($needBackupDir) {
            self::run(self::genMkdirCmd("{$path}/backups"));
        }

        return $path;
    }

    /**
     * Make & return the config dump path.
     *
     * <pre>
     * "/tmp/fs2-export-{$userId}-" . SzTime::getYmdDate() . "-" . uniqid()
     * </pre>
     *
     * @param string $userId
     * @return string $path
     */
    private function getDumpPath($userId)
    {
        $rand = uniqid();
        $path = "/tmp/fs2-export-{$userId}-" . SzTime::getYmdDate() . "-{$rand}";
        self::run(self::genMkdirCmd($path));
        self::run(self::genMkdirCmd("{$path}/json"));

        return $path;
    }

    /**
     * Get the config dump tar package file name.
     *
     * <pre>
     * "fs2-export-{$userId}-" . SzTime::getYmdDate() . '.tar.gz'
     * </pre>
     *
     * @param string $userId
     * @return string
     */
    private function getDumpTarName($userId)
    {
        return "fs2-export-{$userId}-" . SzTime::getYmdDate() . '.tar.gz';
    }

    /**
     * Execute 'exec' command.
     *
     * @param string $command
     * @param array $output default array()
     * @return mixed
     */
    public static function run($command, &$output = array())
    {
        SzLogger::get()->debug('COMMAND: ' . $command);
        $result = null;
        if ($output) {
            $result = exec($command, $output);
        } else {
            $result = exec($command);
        }
        return $result;
    }

    /**
     * Generate command "mkdir" of a directory.
     *
     * @param string $path
     * @return string
     */
    public static function genMkdirCmd($path)
    {
        return "mkdir -p {$path}";
    }


    /**
     * Generate command "tar -zcvf".
     *
     * @param string $tarName *.tar.gz without path
     * @param string $compressPath
     * @param array $excludeFiles
     * <pre>
     * array(excludeFileName, ..., excludeFileName)
     * </pre>
     * @return string
     */
    public static function genTarCreateCmd($tarName, $compressPath, $excludeFiles = array())
    {
        $cmd = "tar -zcvf {$tarName} {$compressPath}";

        if ($excludeFiles && is_array($excludeFiles)) {
            foreach ($excludeFiles as $excludeFile) {
                $cmd .= " --exclude={$excludeFile}";
            }
        } else if ($excludeFiles) {
            $cmd .= " --exclude={$excludeFiles}";
        }

        return $cmd;
    }

    /**
     * Generate command "tar -zxvf".
     *
     * @param string $tarName
     * @param string $extractPath
     * @return string
     */
    public static function genTarExtractCmd($tarName, $extractPath)
    {
        return "tar -zxvf {$tarName} -C {$extractPath}";
    }

    /**
     * Dump configs into json config file.
     *
     * @param string $filePath
     * @param string $fileName
     * @param array $configs
     * @return void
     */
    private function dumpJson($filePath, $fileName, $configs)
    {
        $file = new SzFile();
        $file->openFile($filePath, $fileName, SzFile::MODE_WRITE);
        $file->writeFile(
            json_encode($configs)
        );
        $file->closeFile();
    }

    /**
     * update depend column
     *
     * @param array $voArray
     * @param string $dependColumnName
     * @param array $dependColumnConfig
     * <pre>
     * array(
     *      array(
     *          'condition' => array(
     *              $columnName => array($columnData, ...), // $columnData is string or int, and array will like a json string "[1,2,3]"
     *              ...
     *          ),
     *          'dependColumn' => array(
     *              $dependOrmName => $dependOrmColumnName,
     *          )
     *      ),
     *      ...
     * )
     * </pre>
     * @return mixed $columnData
     */
    public function updateDependColumn($voArray, $dependColumnName, $dependColumnConfig)
    {
        $dependColumns = null;
        foreach ($dependColumnConfig as $conditionConfig) {
            $hasMatching = true; // 与当前orm中的条件有一项不复合，则不更新
            if (!is_null($conditionConfig['condition'])) {
                foreach ($conditionConfig['condition'] as $columnName => $columnDatas) {
                    if (!in_array($voArray[$columnName], $columnDatas)) {
                        $hasMatching = false;
                        break;
                    }
                }
            }
            if ($hasMatching) {
                $dependColumns = $conditionConfig['dependColumn'];
            }
        }
        if (!is_null($dependColumns)) {
            // 处理数据更新
            list($dependOrmName) = each($dependColumns);
            $pkId = $voArray[$dependColumnName];
            if (SzUtility::checkArrayKey($dependOrmName, self::$insertOrmData)
                && SzUtility::checkArrayKey($pkId, self::$insertOrmData[$dependOrmName])) {
                $voArray[$dependColumnName] = self::$insertOrmData[$dependOrmName][$pkId];
            }
        }

        return $voArray[$dependColumnName];
    }

    /**
     * 测试导入是否成功
     *
     * @param int $userId
     * @throws SzException 30606
     * @return boolean
     */
    public function importTest($userId)
    {
        $cacheInfo = SzContextFactory::get()->getAppCache()->get('ADMIN-IMPORT:' . $userId);
        if ($cacheInfo) {
            throw new SzException(30606, null, ModuleGmConst::MODULE_NAME);
        }
        return true;
    }

    /**
     * get player userId
     *
     * @param int $platformId
     * @throws SzException 30607
     * @return int $userId
     */
    public function getPlayerId($platformId)
    {
        $platform = SzConfig::get()->loadAppConfig('app', 'PLATFORM');
        $playerId = ModuleSnsFactory::init(null, $platform)->portPlayerUserId($platformId);

        if (is_null($playerId)) {
            throw new SzException(30606, array($platformId), ModuleGmConst::MODULE_NAME);
        }

        return $playerId;
    }

    /**
     * Restore mysql data into database.
     *
     * @param string $ormName
     * @param array $voListArray
     * <pre>
     *  array($pkId = $voArray)
     * </pre>
     *
     * @param boolean $needToRestoreDependId default false
     * @throws SzException 30602
     * @return array()
     * <pre>
     *  array($ormName => array($pkId => $newPkId))
     * </pre>
     */
    public function restoreData($ormName, $voListArray, $needToRestoreDependId = false)
    {
        $dependConfigs = SzConfig::get()->loadAppConfig(ModuleGmConst::CONFIG_GM_IMPORT_DEPEND, null, true);

        if (SzUtility::checkArrayKey($ormName, $dependConfigs)
            && !$needToRestoreDependId) { // 如果是处理存在依赖关系的数据，这个值应该为true
            return null; // 存在依赖关系的数据库最后处理，此处跳过
        } else if (SzUtility::checkArrayKey($ormName, $dependConfigs)) {
            $dependConfig = $dependConfigs[$ormName];
        } else {
            $dependConfig = null;
        }

        $return = array();
        $model = SzPersister::get()->getModel($ormName);
        $reflection = new ReflectionClass($model->getVoClassName());

        foreach ($voListArray as $pkId => $voArray) {
            $params = array();
            foreach ($model->getColumns() as $columnName) { // loop according to orm columns order
                if (!SzUtility::checkArrayKey($columnName, $voArray, true)) {
                    throw new SzException(30602, null, ModuleGmConst::MODULE_NAME); // invalid $data
                }
                if (is_array($voArray[$columnName])) {
                    $voArray[$columnName] = json_encode($voArray[$columnName]);
                }

                // 处理insert存在依赖关系的数据
                if ($needToRestoreDependId
                    && SzUtility::checkArrayKey($columnName, $dependConfig)) {
                    $voArray[$columnName] = $this->updateDependColumn($voArray, $columnName, $dependConfig[$columnName]);
                }
                $params[] = $voArray[$columnName];
            }

            // 自增长的主键，需要清除，后面还需要记录新产生的自增长主键
            if (!is_null($model->getAutoIncrColumn())) {
                $params[$model->getAutoIncrColumn()] = null;
            }
            $params[] = true; // insert flag

            /* @var SzAbstractVo $vo */
            $vo = $reflection->newInstanceArgs($params);

            if ($model->getDbType() == SzAbstractDb::DB_TYPE_MYSQL) {
                /* @var SzMySqlModel $model */
                list($affectedRows, $insertedId) = $model->insert($vo);
                if ($affectedRows) {
                    $return[$pkId] = $insertedId;
                } else {
                    throw new SzException(30603, null, ModuleGmConst::MODULE_NAME); // failed
                }
            } else if ($model->getDbType() == SzAbstractDb::DB_TYPE_REDIS) {
                /* @var SzRedisModel $model */
                $insertedId = $model->genAutoIncrId();
                $return[$pkId] = $insertedId;
                $model->setColumnValue($vo, $model->getAutoIncrColumn(), $insertedId);
                $model->save($vo);
            }
        }

        return array($ormName => $return);
    }

    /**
     * Restore data from csv file into database.
     *
     * @param string $fileName
     * @param string $filePath full file name with path
     * @param boolean $needToRestoreDependId default false
     * @throws SzException 30602
     * @return array()
     * <pre>
     *  array($ormName => array($pkId => $newPkId))
     * </pre>
     */
    public function restoreDataFromJsonFile($fileName, $filePath, $needToRestoreDependId = false)
    {
        $ormName = array_shift(explode('.', $fileName));
        $dependConfigs = SzConfig::get()->loadAppConfig(ModuleGmConst::CONFIG_GM_IMPORT_DEPEND, null, true);
        if (SzUtility::checkArrayKey($ormName, $dependConfigs)
            && !$needToRestoreDependId) { // 如果是处理存在依赖关系的数据，这个值应该为true
            return null; // 存在依赖关系的数据库最后处理，此处跳过
        }

        $file = new SzFile();
        $data = $file->readWholeFile($filePath);
        $voListArray = json_decode($data, true);

        return $this->restoreData($ormName, $voListArray, $needToRestoreDependId);
    }
}



